package org.nkumar.logging;

import java.util.logging.Logger;
import java.util.logging.Level;

/**
 * Simple wrapper over JDK Logger to provide the functionality of EasyLogger.<br/>
 * Here is the mapping between logging levels of EasyLogger and JDK Logger.
 * <table>
 * <tr> <th>EasyLogger Level</th><th>JDK Logger Level</th></tr>
 * <tr><td>ERROR</td><td>SEVERE</td></tr>
 * <tr><td>WARN</td><td>WARNING</td></tr>
 * <tr><td>INFO</td><td>INFO</td></tr>
 * <tr><td>CONFIG</td><td>CONFIG</td></tr>
 * <tr><td>DEBUG</td><td>FINE</td></tr>
 * <tr><td>TRACE</td><td>FINER</td></tr>
 * </table>
 */
final class JDKEasyLoggerAdapter implements EasyLogger
{
    @SuppressWarnings({"NonConstantLogger"})
    private final Logger logger;

    JDKEasyLoggerAdapter(final Logger logger)
    {
        this.logger = logger;
    }

    public boolean isErrorEnabled()
    {
        return logger.isLoggable(Level.SEVERE);
    }

    public void error(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.SEVERE))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.SEVERE, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    public void error(final String msg, final Throwable thrown)
    {
        if (logger.isLoggable(Level.SEVERE))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.SEVERE, callerInfo[0], callerInfo[1], msg, thrown);
        }
    }

    public boolean isWarnEnabled()
    {
        return logger.isLoggable(Level.WARNING);
    }

    public void warn(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.WARNING))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.WARNING, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    public void warn(final String msg, final Throwable thrown)
    {
        if (logger.isLoggable(Level.WARNING))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.WARNING, callerInfo[0], callerInfo[1], msg, thrown);
        }
    }

    public boolean isInfoEnabled()
    {
        return logger.isLoggable(Level.INFO);
    }

    public void info(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.INFO))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.INFO, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    public void info(final String msg, final Throwable thrown)
    {
        if (logger.isLoggable(Level.INFO))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.INFO, callerInfo[0], callerInfo[1], msg, thrown);
        }
    }

    public boolean isConfigEnabled()
    {
        return logger.isLoggable(Level.CONFIG);
    }

    public void config(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.CONFIG))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.CONFIG, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    public boolean isDebugEnabled()
    {
        return logger.isLoggable(Level.FINE);
    }

    public void debug(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.FINE))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.FINE, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    public boolean isTraceEnabled()
    {
        return logger.isLoggable(Level.FINER);
    }

    public void trace(final String msg, final Object... params)
    {
        if (logger.isLoggable(Level.FINER))
        {
            final String[] callerInfo = inferCaller();
            logger.logp(Level.FINER, callerInfo[0], callerInfo[1], msg, params);
        }
    }

    private static final String CLASSNAME = JDKEasyLoggerAdapter.class.getName();
    private static final String[] UNKNOWN_CALLER_INFO = new String[]{"Unknown Class", "Unknown Method"};

    /**
     * Returns the name of the class and the method from where the log call was made. <br/>
     * Never returns null, when the calling class cannot be infered it will return
     * <code>{"Unknown Class", "Unknown Method"}</code>. <br/>
     * Actually Logger class does this but it checks which class called Logger class
     * which always happens to be EasyLogger.
     * Code lifted from logger class.
     * @return an array of two strings, classname and method name
     */
    //this method MUST be private
    private static String[] inferCaller()
    {
        try
        {
            final StackTraceElement[] stack = (new Throwable()).getStackTrace();
            // First, search back to a method in the Logger class.
            int ix = 0;
            while (ix < stack.length)
            {
                final StackTraceElement frame = stack[ix];
                final String cname = frame.getClassName();
                if (cname.equals(CLASSNAME))
                {
                    break;
                }
                ix++;
            }
            // Now search for the first frame before the "Logger" class.
            while (ix < stack.length)
            {
                final StackTraceElement frame = stack[ix];
                final String cname = frame.getClassName();
                if (!cname.equals(CLASSNAME))
                {
                    // We've found the relevant frame.
                    return new String[]{cname, frame.getMethodName(), frame.toString()};
                }
                ix++;
            }
        }
        catch (Exception ignore)
        {
            //could not infer the caller info from the stack trace
        }
        return UNKNOWN_CALLER_INFO;
    }

}
